/*
 * Copyright (C) 2010-2014 Enrique Lara (enrique.posix2.lara@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package trs.gtfs;

import static org.junit.Assert.assertNotNull;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.junit.Test;

import trs.gtfs.model.Agency;
import trs.gtfs.model.Route;
import trs.gtfs.model.Stop;
import trs.gtfs.model.StopTime;
import trs.gtfs.model.Trip;
import trs.gtfs.parser.AgencyParser;

//XXX factor out GTFS into standalone class
//XXX create seperate test class to exercise GTFS api for trip calculating.
//XXX add parser for CalendarDates and maybe transfers.
//XXX include calendar* info in trip calculations.
public class GtfsGen extends BaseTest {
    @Test
    public void testGtfsFile() throws Exception {
        System.out.println("ABS:" + new java.io.File("asdf.txt").getAbsolutePath());

        AgencyParser parser = new AgencyParser();
        String rootDir = ".";
        parser.parseFile(rootDir + "/test/src/trs/gtfs/sample.txt");
        //		List<Agency> list = parser.filter(parser.getList(), new StopByCode("3001843"));
        System.out.println("Agencies:" + parser.getList());
    }

    @Test
    public void testParseLots() throws Exception {
        Gtfs gtfs = load("/var/local/gtfs/fairfax_connector_gtfs");

        String startCode = "RHLA2";
        List<Stop> stops = gtfs.getStopByCode(startCode);

        for (Stop stop : stops) {
            System.out.println("Stop[" + startCode + "]:" + stop);
        }

        Stop stop = stops.get(0);

        List<StopTime> stopTimes = gtfs.getStopTimes(stop.getId());
        StopTime stopTime = stopTimes.get(stopTimes.size() / 2); //middle some where
        System.out.println("StopTime:" + stopTime);
        assertNotNull("ArrivalTime", stopTime.getArrivalTime());
        assertNotNull("DepartureTime", stopTime.getDepartureTime());
    }

    @Test
    public void testExtractSubsets() throws Exception {
//		String gtfsDir = "/var/local/gtfs/fairfax_connector_gtfs";
        String gtfsDir = "/var/local/gtfs/test_gtfs";
        Gtfs gtfs = load(gtfsDir);
        timepoint("After Load.");

        String[] routeIds = {
                                "4922",
                                "4923",
                                "4924",
                                "4925",
                                "4926",
                                //				"4880", Trimming down trips
//				"4882", Trimming down trips 
        "4884",
                                "4886",
                                "4888",
                                //				"4889", Trimming down trips 
        "4890",
                                "4892",
        };
        List<Route> routes = new ArrayList<Route>();

        for (String routeId : routeIds) {
            routes.add(gtfs.getRouteById(routeId));
        }

        System.out.println("routes:" + routes.size());

        for (Route route : routes) {
            System.out.println(route);
        }

        timepoint("After Getting Routes.");

        //Get Trips
        List<Trip> trips = gtfs.getTripByRouteIds(routeIds);
        System.out.println("trips:" + trips.size());
        timepoint("After Getting trips.");

        Set<Long> tripIds = new HashSet<Long>();

        for (Trip trip : trips) {
            tripIds.add(trip.getId());
        }

        timepoint("After Collecting tripIds.");

        List<StopTime> stopTimes = gtfs.getStopTimesByTripIds(tripIds);
        System.out.println("stopTimes:" + stopTimes.size());
        timepoint("After Getting stopTimes.");

        Set<Long> stopIds = new HashSet<Long>();

        for (StopTime stopTime : stopTimes) {
            stopIds.add(stopTime.getStopId());
        }

        System.out.println("stopIds:" + stopIds.size());
        timepoint("After Collecting stopIds.");

        List<Stop> stops = gtfs.getStopsByIds(stopIds);
        System.out.println("stops:" + stops.size());
        timepoint("After Getting stops.");

        //XXX wroute out sub-sets to new files.
        // Write new files
        String outputBaseDir = "/tmp";
        String file = "routes.txt";
        String headers = "route_id,agency_id,route_short_name,route_long_name,route_desc,route_type";
        writeItems(outputBaseDir, file, headers, routes, new CsvSerializer<Route>() {
                Object[] toArray(Route o) {
                    return new Object[] {
                               o.getId(),
                               o.getAgencyId(),
                               o.getShortName(),
                               o.getLongName(),
                               o.getDesc(),
                               o.getType()
                           };
                }
            });

        List<Agency> agencies = new ArrayList<Agency>();
        Agency agency = new Agency();
        agency.setId(1l);
        agency.setName("TEST");
        agencies.add(agency);
        file = "agency.txt";
        headers = "agency_id,agency_name"; //,agency_url,agency_timezone,agency_lang,agency_phone";
        writeItems(outputBaseDir, file, headers, agencies, new CsvSerializer<Agency>() {
                Object[] toArray(Agency o) {
                    return new Object[] {
                               o.getId(),
                               o.getName()
                           };
                }
            });

        //file = "calendar.txt";
        //headers = "service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date";
        //writeItems(outputBaseDir, file, headers, calendars, new CsvSerializer<Calendar>() {});
        file = "stops.txt";
        headers = "stop_id,stop_code,stop_name,stop_desc,stop_lat,stop_lon"; //,zone_id,stop_url,location_type,parent_station";
        writeItems(outputBaseDir, file, headers, stops, new CsvSerializer<Stop>() {
                Object[] toArray(Stop o) {
                    return new Object[] {
                               o.getId(),
                               o.getCode(),
                               o.getName(),
                               o.getDesc(),
                               o.getLat(),
                               o.getLon()
                           };
                }
            });

        file = "stop_times.txt";
        headers = "trip_id,arrival_time,departure_time,stop_id,stop_sequence,stop_headsign,pickup_type,drop_off_type"; //,shape_dist_traveled";
        writeItems(outputBaseDir, file, headers, stopTimes, new CsvSerializer<StopTime>() {
                Object[] toArray(StopTime o) {
                    return new Object[] {
                               o.getTripId(),
                               o.getArrivalTime(),
                               o.getDepartureTime(),
                               o.getStopId(),
                               o.getStopSequence(),
                               o.getStopHeadsign(),
                               o.getPickupType(),
                               o.getDropOffType()
                           };
                }
            });

        file = "trips.txt";
        headers = "route_id,service_id,trip_id,trip_headsign,direction_id"; //,block_id,shape_id";
        writeItems(outputBaseDir, file, headers, trips, new CsvSerializer<Trip>() {
                Object[] toArray(Trip o) {
                    return new Object[] {
                               o.getRouteId(),
                               o.getServiceId(),
                               o.getId(),
                               o.getHeadsign(),
                               o.getDirectionId()
                           };
                }
            });

        timepoint("After writing files");
    }

    protected <T> void writeItems(String outputBaseDir, String filename, String headers, List<T> items, CsvSerializer<T> serializer) throws IOException {
        PrintWriter out = new PrintWriter(new FileWriter(new File(new File(outputBaseDir), filename)));
        out.println(headers);

        for (T o : items) {
            String str = serializer.toCsvString(o);

            if (str != null) {
                out.println(str);
            } else {
                System.err.println("item[" + o + "] produced a null CSVString.");
            }
        }

        out.close();
    }

    abstract static class CsvSerializer<T> {
        public final String toCsvString(T o) {
            if (o == null) {
                return null;
            }

            Object[] fields = toArray(o);

            StringBuilder buff = new StringBuilder();

            for (int i = 0; i < fields.length; i++) {
                if (i > 0) {
                    buff.append(",");
                }

                if (fields[i] != null) {
                    buff.append(fields[i]);
                }
            }

            return buff.toString();
        }

        abstract Object[] toArray(T o);
    }

    //	@Test
    public void XXXtestParseHyooge() throws Exception {
        Gtfs gtfs = load("/var/local/gtfs/wmata_gtfs");

        String startCode = "6000902"; //10B 
        String routeId = "2"; //10B
                              //End: Ater:Stop2:Stop[, id=9736, code=6000238, name=COLUMBIA PI & S WALTER REED DR, desc=null, lat=38.862544, lon=-77.087013, zoneId=1, url=null]
                              //XXX add a walking node and then traverse this single trip looking for closest stop including walking.
                              //XXX handle arrival/departure time as double.

        List<Stop> stops = gtfs.getStopByCode(startCode);

        for (Stop stop : stops) {
            System.out.println("Stop[" + startCode + "]:" + stop);
        }

        Stop stop = stops.get(0);

        List<StopTime> stopTimes = gtfs.getStopTimes(stop.getId());
        StopTime stopTime = stopTimes.get(stopTimes.size() / 2); //middle some ware

        for (StopTime tmpStopTime : stopTimes) {
            Trip trip = gtfs.getTripById(tmpStopTime.getTripId());

            if (routeId.equals(trip.getRouteId())) {
                stopTime = tmpStopTime;

                break;
            }
        }

        System.out.println("StopTime:" + stopTime);

        Trip trip = gtfs.getTripById(stopTime.getTripId());
        System.out.println("\tTrip:" + trip);

        Route route = gtfs.getRouteById(trip.getRouteId());
        System.out.println("\tRoute:" + route);

        stopTimes = gtfs.getStopTimesAfter(stopTime);

        stops = new ArrayList<Stop>();

        for (StopTime stopTime2 : stopTimes) {
            System.out.println("After:StopTime:" + stopTime2);

            Stop stop2 = gtfs.getStopById(stopTime2.getStopId());
            System.out.println("\tAter:Stop2:" + stop2);
            stops.add(stop2);
        }

        //

        //		38.978195,-76.926346

        //		stopSequence=31, tripId=19815

        //		// /var/local/gtfs/wmata_gtfs/route_xref.txt
        //		show(AgencyParser.loadFile("/var/local/gtfs/wmata_gtfs/agency.txt"));
        //		show(TripParser.loadFile("/var/local/gtfs/wmata_gtfs/trips.txt"));
        //		show(StopParser.loadFile("/var/local/gtfs/wmata_gtfs/stops.txt"));
        //		// /var/local/gtfs/wmata_gtfs/too_fast.txt
        //		show(RouteParser.loadFile("/var/local/gtfs/wmata_gtfs/routes.txt"));
        //		show(StopTimeParser.loadFile("/var/local/gtfs/wmata_gtfs/stop_times.txt"));
        //		// /var/local/gtfs/wmata_gtfs/calendar_dates.txt
        //		// /var/local/gtfs/wmata_gtfs/shapes.txt
    }

    @Test
    public void testGtfsString() {
        final String N = "\r\n";
        final String AGENCY_TXT = "agency_id,agency_name,agency_url,agency_timezone,agency_lang,agency_phone" + N + ",Fairfax Connector,http://www.fairfaxconnector.com,America/New_York,en,703-339-7200" + N + "";

        AgencyParser parser = new AgencyParser();
        parser.parse(new ByteArrayInputStream(AGENCY_TXT.getBytes()));

        System.out.println("Agencies2:" + parser.getList());
    }

    //	public static void main(String[] args) {
    //		new GtfsGen().testGtfs();
    //	}
}
/*
mvn -o test -Dtest=GtfsGen ; cat ./test/src/trs/gtfs/sample.txt

mvn -o test -Dtest=GtfsGen && cat ./test/src/trs/gtfs/sample.txt  || vi target/surefire-reports/trs.gtfs.GtfsGen.txt

Route
Stop
-> id
-> code - same code may show up more than once.
-> name - same name

StopTime
-> Trip
-> Stop
-> stopSequence - greater than this stoptime

Trip
-> Route
-> Service Id - what days.

 */
